class Compile {
    constructor(el, vm) {
        this.el = this.isElementNode(el) ? el : document.querySelector(el);
        this.vm = vm;
        if (this.el) {
            let fragment = this.node2fragment();
            this.compile(fragment);
            this.el.appendChild(fragment);
        }
    }
    /*辅助方法 */
    isElementNode(node) {
        return node.nodeType === 1;
    }
    isDirective(name) {
        return name.includes('v-');
    }
    node2fragment() {
        let el = this.el;
        let fragment = document.createDocumentFragment();
        let firstChild = el.firstChild;
        while (firstChild) {
            fragment.appendChild(firstChild);
            firstChild = el.firstChild;
        }
        return fragment;
    }
    compile(fragment) {
        let childNodes = fragment.childNodes;
        Array.from(childNodes).forEach((node) => {
            if (this.isElementNode(node)) {
                //元素节点
                this.compileElement(node);
                this.compile(node);
            } else {
                //文本节点
                this.compileText(node);
            }
        });
    }
    compileElement(node) {
        let attrs = node.attributes;
        Array.from(attrs).forEach((attr) => {
            if (this.isDirective(attr.name)) {
                let expr = attr.value;
                let [, type] = attr.name.split("-");
                if (type.includes("on")) {
                    CompileUtil["on"](node, this.vm, expr, type);
                } else {
                    CompileUtil[type](node, this.vm, expr);
                }

            }
        });
    }
    compileText(node) {
        let expr = node.textContent;
        let reg = /\{\{(\w*)\}\}/g;
        if (reg.test(expr)) {
            CompileUtil['text'](node, this.vm, expr);
        }
    }
}


CompileUtil = {
    getVal(vm, expr) {
        expr = expr.split(".");
        return expr.reduce((result, next) => {
            return result[next];
        }, vm.$data);
    },
    getTextVal(vm, expr) {
        let value = expr.replace(/\{\{([^}]+)\}\}/g, (...args) => {
            return this.getVal(vm, args[1]);
        });
        return value;
    },
    text(node, vm, expr) {
        let updateFn = this.updater['textUpdater'];
        let value = this.getTextVal(vm, expr);
        expr.replace(/\{\{([^}]+)\}\}/g, (...args) => {
            new Watcher(vm, args[1], (newValue) => {
                updateFn && updateFn(node, this.getTextVal(vm, expr));
            });
        });
        updateFn && updateFn(node, value);
    },
    model(node, vm, expr) {
        let updateFn = this.updater['modelUpdater'];
        let value = vm.$data[expr];
        new Watcher(vm, expr, (newValue) => {
            updateFn && updateFn(node, newValue);
        });
        node.addEventListener("input", (e) => {
            let newValue = e.target.value;
            vm.$data[expr] = newValue;
        });
        updateFn && updateFn(node, value);
    },

    html(node, vm, expr) {
        let updateFn = this.updater['htmlUpdater'];
        let value = vm.$data[expr];
        new Watcher(vm, expr, (newValue) => {
            updateFn && updateFn(node, newValue);
        });
        updateFn && updateFn(node, value);
    },
    on(node, vm, expr, type) {
        const callback = vm.$methods["clickHandler"];
        if (typeof callback !== "function") {
            return;
        }
        const [, event] = type.split(":")
        node.addEventListener(event, (e) => {
            callback(e);
        });
    },
    updater: {
        //文本更新
        textUpdater(node, value) {
            node.textContent = value
        },
        //输入框更新
        modelUpdater(node, value) {
            node.value = value;
        },
        htmlUpdater(node, value) {
            node.innerHTML = value;
        }
    }
};